<?php

/**
 * @desc		精简版Smarty视图引擎
 * ---------------------------------------------------------------------
 * @author	unphp <unphp@qq.com>
 * @date		2014-03-05
 * @copyright	UnPHP 1.1
 * ---------------------------------------------------------------------
 */

namespace UnPHP\Lib\View;

use UnPHP\Core\ViewAbstract;

class FileSmarty extends ViewAbstract
{

        // 模板文件目录
        public $template_dir   = '';
        // 静态缓存目录
        public $cache_dir      = '';
        // 编译缓存目录
        public $compile_dir    = '';
        // 缓存更新时间, 默认 3600 秒
        public $cache_lifetime = 3600;
        // 不缓存，直接输出。。。
        public $is_cache       = true;
        // 是否开启静态缓存
        public $caching        = true;
        // 是否开启编译缓存
        public $force_compile  = true;
        public $template       = array();
        public $_var           = array();
        public $_UnPHPhash     = '554fcae493e564ee0dc75bdf2ebf94ca_xiaotangren';
        public $_foreach       = array();
        public $_current_file  = '';
        public $_expires       = 0;
        public $_errorlevel    = 0;
        public $_nowtime       = null;
        public $_checkfile     = true;
        public $_foreachmark   = '';
        public $_seterror      = 0;
        public $_temp_key      = array();  // 临时存放 foreach 里 key 的数组
        public $_temp_val      = array();  // 临时存放 foreach 里 item 的数组
        public $_js_tpl_val = array();
        public $filename       = array();
        // 静态缓存文件“是否存在”
        protected $cached      = false;
        protected $_start_tag  = '{';
        protected $_end_tag    = '}';
        protected $_js_version = '0.0.1';
        static public $smarty  = null;

        public function init($conf = array())
        {
                $this->_errorlevel    = error_reporting();
                $this->_nowtime       = time();
                $this->cache_lifetime = isset($conf['lifetime']) ? $conf['lifetime'] : 3600;
                $this->is_cache       = isset($conf['is_cache']) ? $conf['is_cache'] : true;  // 默认缓存
                // 所谓编译缓存，指将模板smarty标签，翻译成php和html混合的文件后，生成的缓存。
                $this->force_compile  = isset($conf['compileCaching']) ? $conf['compileCaching'] : true;   // 默认开启编译缓存
                $this->caching        = isset($conf['htmlCaching']) ? $conf['htmlCaching'] : true;   // 默认开启静态缓存
                if ($this->is_cache && $this->caching)
                {
                        $this->cache_dir = rtrim($conf['tempPath'], '/\\') . DIRECTORY_SEPARATOR . 'html' . DIRECTORY_SEPARATOR;
                }
                if ($this->is_cache && $this->force_compile)
                {
                        $this->compile_dir = rtrim($conf['tempPath'], '/\\') . DIRECTORY_SEPARATOR . 'compile' . DIRECTORY_SEPARATOR;
                }

                $this->_start_tag = $this->escapeTag(isset($conf['startTag']) ? $conf['startTag'] : '{');   // 开始标签
                $this->_end_tag   = $this->escapeTag(isset($conf['endTag']) ? $conf['endTag'] : '}');         // 结束标签
                $this->_js_version = isset($conf['jsVersion']) ? $conf['jsVersion'] : '1.0.0';

                if (!is_readable($conf['tempPath']))
                {
                        if (!@mkdir($conf['tempPath'], 0777, TRUE))
                        {
                                throw new SmartyException('Invalid path provided,in that themes\'s dir of " ' . $conf['tempPath'] . ' "!');
                        }
                }
                if ($this->is_cache && $this->caching && !is_readable($this->cache_dir))
                {
                        if (!@mkdir($this->cache_dir, 0777, TRUE))
                        {
                                throw new SmartyException('There\'s not enough permissions for create file, in that temp\'s dir of ' . $conf['tempPath'] . $this->cache_dir . '!');
                        }
                }
                if ($this->is_cache && $this->force_compile && !is_readable($this->compile_dir))
                {
                        if (!@mkdir($this->compile_dir, 0777, TRUE))
                        {
                                throw new SmartyException('There\'s not enough permissions for create file, in that temp\'s dir of ' . $conf['tempPath'] . '!');
                        }
                }
                return true;
        }

        /**
         * Set the path to the templates
         *
         * @param string $path The directory to set as the path.
         * @return void
         */
        public function setScriptPath($path)
        {
                try
                {
                        if (is_readable($path))
                        {
                                $this->template_dir = rtrim($path, '/\\') . DIRECTORY_SEPARATOR;
                                return;
                        }
                }
                catch (Exception $exc)
                {
                        throw new SmartyException('Invalid path provided,in that themes\'s dir of " ' . $path . ' "!' . $exc->getMessage());
                }
        }

        /**
         * Retrieve the current template directory
         *
         * @return string
         */
        public function getScriptPath()
        {
                return $this->template_dir;
        }

        /**
         * Processes a template and returns the output.
         *
         * @param string $name The template to process.
         * @return string The output.
         */
        public function render($name, $value = NULL)
        {
                return $this->fetch($name, $value);
        }

        /**
         * 注册变量
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param mix $tpl_var        	
         * @param mix $value        	
         * @return void
         */
        public function assign($tpl_var, $value = '')
        {
                if (is_array($tpl_var))
                {
                        foreach ($tpl_var as $key => $val)
                        {
                                if ($key != '')
                                {
                                        $this->_var[$key] = $val;
                                }
                        }
                }
                else
                {
                        if ($tpl_var != '')
                        {
                                $this->_var[$tpl_var] = $value;
                        }
                }
        }

        /**
         * 显示页面函数
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $filename        	
         * @param sting $cache_id        	
         * @return void
         */
        public function display($filename, $cache_id = '')
        {
                if ($this->_response_hander)
                {
                        foreach ($this->_response_hander as $func)
                        {
                                $func();
                        }
                }
                $this->_checkfile = false;
                $out              = $this->fetch($filename, $cache_id);
                if (strpos($out, $this->_echash) !== false)
                {
                        $k = explode($this->_echash, $out);
                        foreach ($k as $key => $val)
                        {
                                if (($key % 2) == 1)
                                {
                                        $k[$key] = $this->insert_mod($val);
                                }
                        }
                        $out = implode('', $k);
                }
                echo $out;
        }

        /**
         * 处理模板文件
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $filename        	
         * @param sting $cache_id        	
         * @return sring
         */
        public function fetch($filename, $cache_id = '')
        {
                if (strncmp($filename, 'str:', 4) == 0)
                {
                        $out = $this->_eval($this->fetch_str(substr($filename, 4)));
                }
                else
                {
                        $filename_cp = $filename;
                        // 检测模板文件是否存在
                        if ($this->_checkfile)
                        {
                                if (!file_exists($filename))
                                {
                                        $filename = $this->template_dir . $this->_ds . $filename;
                                }
                        }
                        else
                        {
                                $filename = $this->template_dir . $this->_ds . $filename;
                        }
                        // 不作任何缓存（编译缓存，静态缓存），直接输出。。。
                        if ($this->direct_output)
                        {
                                $this->_current_file = $filename;
                                $out                 = $this->_eval($this->fetch_str(file_get_contents($filename)));
                        }
                        // 进行缓存
                        else
                        {
                                // 如果有静态缓存，并且设置了缓存，则直接输出。
                                if ($cache_id && $this->caching && $this->cached)
                                {
                                        $out = $this->template_out;
                                }
                                // 否则编译生成。
                                else
                                {
                                        if (!in_array($filename, $this->template))
                                        {
                                                $this->template[] = $filename;
                                        }
                                        // 进行编译
                                        $out = $this->make_compiled($filename);
                                        // 进行静态缓存
                                        if ($cache_id && $this->caching)
                                        {
                                                $out            = $this->doCreateCache($filename_cp, $cache_id, str_replace("\r", '', $out));
                                                $this->template = array();
                                        }
                                }
                        }
                }
                return $out; // 返回html数据
        }

        public function fetch_file($file, $isstr = FALSE)
        {
                // 检测模板文件是否存在
                if ($this->_checkfile)
                {
                        if (!file_exists($file))
                        {
                                $file = $this->template_dir . $this->_ds . $file;
                        }
                }
                else
                {
                        $file = $this->template_dir . $this->_ds . $file;
                }
                $this->_current_file = $file;
                $content             = file_get_contents($file);
                if ($isstr)
                {
                        $content = 'str:' . $content;
                }
                $out = $this->_eval($this->fetch_str($content));
                return $out; // 返回html数据
        }

        /**
         * 编译模板函数
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $filename        	
         * @return sring 编译后文件内容
         */
        public function make_compiled($filename)
        {
                if ($this->force_compile == false)
                {
                        $this->_current_file = $filename;
                        $source              = $this->_eval($this->fetch_str(file_get_contents($filename)));
                }
                else
                {
                        $compile_dir_file = str_replace($this->template_dir, '/', $filename);
                        $name             = $this->compile_dir . $compile_dir_file . '.php';
                        $expires          = 0;
                        if (file_exists($name))
                        {
                                $filestat = @stat($name);
                                $expires  = $filestat['mtime'];
                        }
                        // -----------------------------------
                        // 模板文件日期等信息
                        $filestat = @stat($filename);
                        // 判断模板是否更新：对比“缓存生成时间”和“模板修改时间”
                        if ($filestat['mtime'] <= $expires)
                        {
                                $source = $this->_require($name);
                        }
                        // 生成（或覆盖）编译缓存。
                        else
                        {
                                $this->_current_file = $filename;
                                $fetch_str           = $this->fetch_str(file_get_contents($filename));
                                $this->autoMkdir($name);
                                if (file_put_contents($name, $fetch_str, LOCK_EX) === false)
                                {
                                        trigger_error('can\'t write:' . $name);
                                }
                                $source = $this->_eval($fetch_str);
                        }
                }
                // 返回编译内容
                return $source;
        }

        protected function _eval($content)
        {
                ob_start();
                eval('?' . '>' . trim($content));
                $content = ob_get_contents();
                ob_end_clean();

                return $content;
        }

        protected function _require($filename)
        {
                ob_start();
                include $filename;
                $content = ob_get_contents();
                ob_end_clean();

                return $content;
        }

        /**
         * 处理字符串函数
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $source        	
         * @return sring
         */
        public function fetch_str($source)
        {
                $source = preg_replace("/<\?[^><]+\?>|<\%[^><]+\%>|<script[^>]+language[^>]*=[^>]*php[^>]*>[^><]*<\/script\s*>/iU", "", $source);
                // smarty的注释语法
                $source = preg_replace("/" . $this->_start_tag . "\*.*?\*" . $this->_end_tag . "/is", "", $source);
                // $source = preg_replace("/<\%[^><]+\%>|<script[^>]+language[^>]*=[^>]*php[^>]*>[^><]*<\/script\s*>/iU", "", $source);
                //过滤js模板
                $source = preg_replace_callback("/" . $this->_start_tag . "#(.*)#" . $this->_end_tag . "/is", "self::callback_js_tpl_replace", $source);
                //
                $source = preg_replace_callback("/<script[\s]+(?:type=\"text\/javascript\"[\s]+){0,1}src=\"([^><\'\"\?]*)\"[\s]*>[^><]*<\/script[\s]*>/is", "self::callback_js_version", $source);
                //<link rel="stylesheet" href="http://spapa.wicp.net:8091/Static/bootstrap/3.3.5/css/bootstrap.min.css">
                $source = preg_replace_callback("/<link[\s]+(?:rel=\"stylesheet\"[\s]+){0,1}href=\"([^><\'\"\?]*)\"[\s]*[\/]*[\s]*>/is", "self::callback_css_version", $source);
                // smarty 起始标签
                $source = preg_replace_callback("/" . $this->_start_tag . "([^" . $this->_start_tag . $this->_end_tag . "\n]*)" . $this->_end_tag . "/", "self::callback_select", $source);
                //还原js模板引擎的内容
                if (count($this->_js_tpl_val) > 0)
                {
                        foreach ($this->_js_tpl_val as $k => $v)
                        {
                                $source = preg_replace("/".$this->_start_tag . "#".$k."#" . $this->_end_tag."/", $v, $source);
                        }
                }
                return $source;
        }

        public function callback_js_tpl_replace($matches)
        {
                $hash = hash('ripemd160', $matches[1]);
                $this->_js_tpl_val[$hash] = $matches[1];
                return "{{#".$hash."#}}";
        }

        public function callback_js_version($matches)
        {
                $src = $matches[1];
                return '<script type="text/javascript" src="'.$src.'?version='.$this->_js_version.'"></script>';
        }

        public function callback_css_version($matches)
        {
                $src = $matches[1];
                return '<link rel="stylesheet" href="'.$src.'?version='.$this->_js_version.'" />';
        }

        /**
         * 判断是否缓存
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $filename        	
         * @param sting $cache_id        	
         * @return bool
         */
        public function is_cached($filename, $cache_id = '')
        {
                if ($this->caching == true && $this->direct_output == false)
                {
                        $this->cached = true;
                        $cachefile    = $this->getCachefilepath($filename, $cache_id);
                        if (file_exists($cachefile) && $data         = @file_get_contents($cachefile))
                        {
                                $data     = substr($data, 13);
                                $pos      = strpos($data, '<');
                                $paradata = substr($data, 0, $pos);
                                $para     = @unserialize($paradata);
                                // 如果 “现在的时间”>缓存“过期时间” ，那么，缓存失效！
                                if ($para === false || $this->_nowtime > $para['expires'])
                                {
                                        $this->cached = false;
                                        return false;
                                }
                                $this->_expires     = $para['expires'];
                                $this->template_out = substr($data, $pos);
                                // 遍历加载进来的每个“子模板”，对比“子模板”修改时间与缓存创建时间。
                                foreach ($para['template'] as $val)
                                {
                                        $stat = @stat($val);
                                        // 如果“子模板”修改时间 大于 “缓存创建时间”，那么，缓存失效！
                                        if ($para['maketime'] < $stat['mtime'])
                                        {
                                                $this->cached = false;
                                                return false;
                                        }
                                }
                        }
                        else
                        {
                                $this->cached = false;
                                return false;
                        }
                        return true;
                }
                else
                {
                        return false;
                }
        }

        /**
         * 处理{}标签
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $tag        	
         * @return sring
         */
        protected function select($tag)
        {
                $tag = stripslashes(trim($tag));
                if (empty($tag))
                {
                        return '{}';
                }
                // 注释部分
                elseif ($tag{0} == '*' && substr($tag, - 1) == '*')
                {
                        return '';
                }
                // 变量
                elseif ($tag{0} == '$')
                {
                        if ($tag{1} != '(') // 避免与JQuery中的选择器---"{$" 冲突
                                return '<?php echo ' . $this->get_val(substr($tag, 1)) . '; ?>';
                }
                // 结束 tag
                elseif ($tag{0} == '/')
                {
                        switch (substr($tag, 1))
                        {
                                case 'if':
                                        return '<?php endif; ?>';
                                        break;

                                case 'foreach':
                                        if ($this->_foreachmark == 'foreachelse')
                                        {
                                                $output = '<?php endif; unset($_from); ?>';
                                        }
                                        else
                                        {
                                                array_pop($this->_patchstack);
                                                $output = '<?php endforeach; endif; unset($_from); ?>';
                                        }
                                        /* $output .= "<?php \$this->pop_vars();; ?>"; */

                                        return $output;
                                        break;

                                case 'literal':
                                        return '';
                                        break;
                                // case '_php':
                                // return ' ? >';
                                // break;
                                default:
                                        return '{{' . $tag . '}}';
                                        break;
                        }
                }
                // 其他标签
                else
                {
                        // else if 标签
                        if ('else if' === (substr($tag, 0, 7)))
                        {
                                return $this->_compile_if_tag(substr($tag, 7), true);
                        }
                        $tag_temp = explode(' ', $tag); // 分割成数组
                        $tag_sel  = array_shift($tag_temp); // 取出数组中第一个元素，并从数组中删除它
                        switch ($tag_sel)
                        {
                                case 'if':

                                        return $this->_compile_if_tag(substr($tag, 3));
                                        break;

                                case 'else':

                                        return '<?php else: ?>';
                                        break;

                                case 'elseif':

                                        return $this->_compile_if_tag(substr($tag, 7), true);
                                        break;

                                case 'foreachelse':
                                        $this->_foreachmark = 'foreachelse';

                                        return '<?php endforeach; else: ?>';
                                        break;

                                case 'foreach':
                                        $this->_foreachmark = 'foreach';
                                        if (!isset($this->_patchstack))
                                        {
                                                $this->_patchstack = array();
                                        }
                                        return $this->_compile_foreach_start(substr($tag, 8));
                                        break;

                                case 'assign':
                                        $t = $this->get_para(substr($tag, 7), 0);
                                        if ($t['value']{0} == '$')
                                        {
                                                /* 如果传进来的值是变量，就不用用引号 */
                                                $tmp = '$this->assign(\'' . $t['var'] . '\',' . $t['value'] . ');';
                                        }
                                        else
                                        {
                                                $tmp = '$this->assign(\'' . $t['var'] . '\',\'' . addcslashes($t['value'], "'") . '\');';
                                        }
                                        // $tmp = $this->assign($t['var'], $t['value']);

                                        return '<?php ' . $tmp . ' ?>';
                                        break;

                                case 'include':
                                        // $t = $this->get_para(substr($tag, 8), 1);
                                        // 修复file=$xxx不支持变量的bug
                                        $t = $this->get_para(substr($tag, 8), 0);
                                        if (preg_match('/file[\s]?=[\s]?\$/ie', substr($tag, 8)))
                                        {
                                                return '<?php echo $this->fetch(' . "{$t['file']}" . '); ?>';
                                        }
                                        else
                                        {
                                                return '<?php echo $this->fetch(' . "'{$t['file']}'" . '); ?>';
                                        }
                                        break;

                                case 'insert_scripts':
                                        $t = $this->get_para(substr($tag, 15), 0);

                                        return '<?php echo $this->smarty_insert_scripts(' . $this->make_array($t) . '); ?>';
                                        break;

                                case 'create_pages':
                                        $t = $this->get_para(substr($tag, 13), 0);

                                        return '<?php echo $this->smarty_create_pages(' . $this->make_array($t) . '); ?>';
                                        break;

                                case 'insert':
                                        $t   = $this->get_para(substr($tag, 7), 0);
                                        // $out = "<?php \n" . '$k = ' . preg_replace("/(\'\\$[^,]+)/e", "stripslashes(trim('\\1','\''));", var_export($t, true)) . ";\n";
                                        $out = "<?php \n" . '$k = ' . preg_replace_callback("/(\'\\$[^,]+)/", function ($match) {
                                                        return stripslashes(trim($match[1], '\''));
                                                }, var_export($t, true)) . ";\n";
                                        $out .= 'echo $this->_echash . $k[\'name\'] . \'|\' . serialize($k) . $this->_echash;' . "\n?>";

                                        return $out;
                                        break;

                                case 'literal':
                                        return '';
                                        break;

                                case 'cycle':
                                        $t = $this->get_para(substr($tag, 6), 0);

                                        return '<?php echo $this->cycle(' . $this->make_array($t) . '); ?>';
                                        break;

                                case 'html_options':
                                        $t = $this->get_para(substr($tag, 13), 0);

                                        return '<?php echo $this->html_options(' . $this->make_array($t) . '); ?>';
                                        break;

                                case 'html_select_date':
                                        $t = $this->get_para(substr($tag, 17), 0);

                                        return '<?php echo $this->html_select_date(' . $this->make_array($t) . '); ?>';
                                        break;

                                case 'html_radios':
                                        $t = $this->get_para(substr($tag, 12), 0);

                                        return '<?php echo $this->html_radios(' . $this->make_array($t) . '); ?>';
                                        break;

                                case 'html_select_time':
                                        $t = $this->get_para(substr($tag, 12), 0);

                                        return '<?php echo $this->html_select_time(' . $this->make_array($t) . '); ?>';
                                        break;
                                // case '_php':
                                // return '<?php ';
                                // break;
                                case 'num_float_rand':
                                        $t   = $this->get_para(substr($tag, 9), 0);
                                        $min = intval($t['min']);
                                        $max = intval($t['max']);
                                        $f   = intval($t['f']);
                                        return '<?php printf("%.' . $f . 'f",' . $min . '+ mt_rand() / mt_getrandmax()*(' . $max . '-' . $min . ')); ?>';
                                        break;
                                default:
                                        return '{{' . $tag . '}}';
                                        break;
                        }
                }
        }

        /**
         * 处理insert外部函数/需要include运行的函数的调用数据
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $val        	
         * @param int $type        	
         * @return array
         */
        protected function get_para($val, $type = 1, &$rs_arr = array()) // 处理insert外部函数/需要include运行的函数的调用数据
        {
                $pa = $this->str_trim($val);
                foreach ($pa as $value)
                {
                        if (strrpos($value, '='))
                        {
                                list ($a, $b) = explode('=', str_replace(array(
                                    ' ',
                                    '&quot;'
                                                ), '', $value));
                                if ($b{0} == '$')
                                {
                                        switch ($type)
                                        {
                                                case 1:

                                                        eval('$para[\'' . $a . '\']=' . $this->get_val(substr($b, 1)) . ';');
                                                        break;
                                                case 2:
                                                        $para[$a]   = $this->get_val(substr($b, 1));
                                                        $rs_arr[$a] = substr($b, 1);
                                                        break;
                                                default:
                                                        $para[$a]   = $this->get_val(substr($b, 1));
                                                        break;
                                        }
                                }
                                else
                                {
                                        $para[$a] = str_replace(array(
                                            '"',
                                            "'"
                                                ), '', $b);
                                }
                        }
                }

                return $para;
        }

        /**
         * 处理smarty标签中的变量标签
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $val        	
         * @return bool
         */
        protected function get_val($val)
        {
                if (strrpos($val, '[$') !== false)
                {
                        // $val = preg_replace("/\[(\$[^\$\[\]]*)\]/eis", "'['.str_replace('$','\$','\\1').']'", $val);
                        $val = preg_replace_callback("/\[(\$[^\$\[\]]*)\]/is", function ($match) {
                                return '[' . str_replace('$', '\$', $match[1]) . ']';
                        }, $val);
                }
                if (strrpos($val, "['") !== false || strrpos($val, '["') !== false)
                {
                        // $val = preg_replace("/\[[\']([^\[\]]*)[\']\]/eis", "'.\\1'", $val);
                        // $val = preg_replace("/\[[\"]([^\[\]]*)[\"]\]/eis", "'.\\1'", $val);
                        $val = preg_replace_callback("/\[[\']([^\[\]]*)[\']\]/is", function ($match) {
                                return "." . strval($match[1]);
                        }, $val);
                        $val = preg_replace_callback("/\[[\"]([^\[\]]*)[\"]\]/is", function ($match) {
                                return "." . strval($match[1]);
                        }, $val);
                }
                //
                if (strrpos($val, '|') !== false)
                {
                        $moddb = explode('|', $val);
                        $val   = array_shift($moddb);
                }
                else
                {
                        if (strrpos($val, '@') !== false)
                        {
                                $moddb2 = explode('@', $val);
                                $val    = array_shift($moddb2);
                        }
                }
                //
                if (empty($val))
                {
                        return '';
                }
                if (strpos($val, '[$') !== false)
                {
                        $val = '$' . $val;
                        // $val = preg_replace("/\.([^\.\[\]\$]+)/ie","\$this->preg_var_dian('$1')",$val);
                        $val = preg_replace_callback("/\.([^\.\[\]\$]+)/i", "self::callback_preg_var_dian", $val);
                        // $p = preg_replace("/\\$([\w]+)/ise","\$this->make_var('$1')",$val);
                        $p   = preg_replace_callback("/\\$([\w]+)/is", "self::callback_make_var", $val);
                        // var_dump($p);exit;
                        // $all = explode('.$', $val);
                        // foreach ($all AS $key => $val)
                        // {
                        // $all[$key] = $key == 0 ? $this->make_var($val) : '[' . $this->make_var($val) . ']';
                        // }
                        // $p = implode('', $all);
                }
                else
                {
                        $p = $this->make_var($val);
                }

                if (!empty($moddb))
                {
                        foreach ($moddb as $key => $mod)
                        {
                                $s = explode(':', $mod);
                                switch ($s[0])
                                {
                                        case 'escape':
                                                $s[1] = trim($s[1], '"');
                                                if ($s[1] == 'html')
                                                {
                                                        $p = 'htmlspecialchars(' . $p . ')';
                                                }
                                                elseif ($s[1] == 'url')
                                                {
                                                        $p = 'urlencode(' . $p . ')';
                                                }
                                                elseif ($s[1] == 'decode_url')
                                                {
                                                        $p = 'urldecode(' . $p . ')';
                                                }
                                                elseif ($s[1] == 'quotes')
                                                {
                                                        $p = 'addslashes(' . $p . ')';
                                                }
                                                elseif ($s[1] == 'u8_url')
                                                {
                                                        if (EC_CHARSET != 'utf-8')
                                                        {
                                                                $p = 'urlencode(ecs_iconv("' . EC_CHARSET . '", "utf-8",' . $p . '))';
                                                        }
                                                        else
                                                        {
                                                                $p = 'urlencode(' . $p . ')';
                                                        }
                                                }
                                                elseif ($s[1] == 'js_str')
                                                {
                                                        $p = 'addslashes(str_replace(array("\r","\n")," ",' . $p . '))';
                                                }
                                                else
                                                {
                                                        $p = 'htmlspecialchars(' . $p . ')';
                                                }
                                                break;

                                        case 'nl2br':
                                                $p = 'nl2br(' . $p . ')';
                                                break;

                                        case 'default':
                                                $s[1] = $s[1]{0} == '$' ? $this->get_val(substr($s[1], 1)) : "'$s[1]'";
                                                $p    = 'empty(' . $p . ') ? ' . $s[1] . ' : ' . $p;
                                                break;

                                        case 'truncate':
                                                $p = 'sub_str(' . $p . ",$s[1])";
                                                break;

                                        case 'strip_tags':
                                                $p    = 'strip_tags(' . $p . ')';
                                                break;
                                        case 'count_characters':
                                                $p    = 'strlen(' . $p . ')';
                                                break;
                                        case 'int_add':
                                                $p    = 'intval(' . $p . ')+intval(' . $s[1] . ')';
                                                break;
                                        case 'replace':
                                                $s[1] = trim($s[1], '"');
                                                $s[2] = trim($s[2], '"');
                                                $p    = 'str_replace(' . $s[1] . ', ' . $s[2] . ',' . $p . ')';
                                                break;
                                        case 'date':
                                                $str  = trim(substr(implode(':', $s), 5));
                                                $p    = 'date("' . $str . '",' . $p . ')';
                                                break;
                                        case 'len':
                                                $p    = 'count(' . $p . ')';
                                                break;
                                        case 'utf8_substr':
                                                $s[1] = intval($s[1]);
                                                $p    = 'smarty_substr(' . $p . ', ' . $s[1] . ')';
                                                break;
                                        case 'dxdy':
                                                if (substr($s[1], 0, 1) == '$')
                                                {
                                                        $p = $p . '*intval(' . $this->get_val(substr($s[1], 1)) . ')';
                                                }
                                                else
                                                {
                                                        $p = $p . '*intval(' . $s[1] . ')';
                                                }
                                                break;
                                        default:
                                                // code...
                                                break;
                                }
                        }
                }

                return $p;
        }

        /**
         * 处理去掉$的字符串
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $val        	
         * @return bool
         */
        protected function make_var($val)
        {
                if (strrpos($val, '.') === false)
                {
                        if (isset($this->_var[$val]) && isset($this->_patchstack[$val]))
                        {
                                $val = $this->_patchstack[$val];
                        }
                        $p = '$this->_var[\'' . $val . '\']';
                }
                else
                {
                        $t         = explode('.', $val);
                        $_var_name = array_shift($t);
                        if (isset($this->_var[$_var_name]) && isset($this->_patchstack[$_var_name]))
                        {
                                $_var_name = $this->_patchstack[$_var_name];
                        }
                        if ($_var_name == 'smarty')
                        {
                                $p = $this->_compile_smarty_ref($t);
                        }
                        else
                        {
                                $p = '$this->_var[\'' . $_var_name . '\']';
                        }
                        foreach ($t as $val)
                        {
                                // 修复bug xtr
                                // $val = str_replace("'", "", $val);
                                $val = trim($val, '\'"');
                                $p .= '[\'' . $val . '\']';
                        }
                }

                return $p;
        }

        private function callback_select($matches)
        {
                return $this->select($matches[1]);
        }

        protected function callback_preg_var_dian($matchs)
        {
                $val = trim($matchs[1], '\'"');
                $val = '[\'' . $val . '\']';
                return $val;
        }

        private function callback_make_var($matchs)
        {
                return $this->make_var($matchs[1]);
        }

        /**
         * 判断变量是否被注册并返回值
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $name        	
         * @return mix
         */
        public function &get_template_vars($name = null)
        {
                if (empty($name))
                {
                        return $this->_var;
                }
                elseif (!empty($this->_var[$name]))
                {
                        return $this->_var[$name];
                }
                else
                {
                        $_tmp = null;

                        return $_tmp;
                }
        }

        /**
         * 处理if标签
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $tag_args        	
         * @param bool $elseif        	
         * @return string
         */
        protected function _compile_if_tag($tag_args, $elseif = false)
        {
                // preg_match_all('/\-?\d+[\.\d]+|\'[^\'|\s]*\'|"[^"|\s]*"|[\$\w\.]+|!==|===|==|!=|<>|<<|>>|<=|>=|&&|\|\||\(|\)|,|\!|\^|=|&|<|>|~|\||\%|\+|\-|\/|\*|\@|\S/', $tag_args, $match);
                // 增加支持类似与 {if $cat_desc|count_characters:true > 500} 的标签
                preg_match_all('/\-?\d+[\.\d]+|\'[^\'|\s]*\'|"[^"|\s]*"|[\$\w\.]+(?:\|[\w]+\:[\w]+)?|!==|===|==|!=|<>|<<|>>|<=|>=|&&|\|\||\(|\)|,|\!|\^|=|&|<|>|~|\||\%|\+|\-|\/|\*|\@|\S/', $tag_args, $match);
                $tokens      = $match[0];
                // make sure we have balanced parenthesis
                $token_count = array_count_values($tokens);
                if (!empty($token_count['(']) && $token_count['('] != $token_count[')'])
                {
                        // $this->_syntax_error('unbalanced parenthesis in if statement', E_USER_ERROR, __FILE__, __LINE__);
                }
                // start
                // update by：xtr 2014-12-18
                // 增加功能：
                // {{foreach ... }}
                // ...
                // {{if $v@first}}
                // ...
                // {{/if}}
                // {{/foreach}}
                if (3 === count($tokens) && '$' === $tokens[0][0] && '@' === $tokens[1])
                {
                        $condition = $this->get_val('smarty.foreach.' . $this->_foreach_val_map[$this->get_val(substr($tokens[0], 1))] . '.first');
                        if ($elseif)
                        {
                                return '<?php elseif (' . $condition . '): ?>';
                        }
                        else
                        {
                                return '<?php if (' . $condition . '): ?>';
                        }
                }
                // end

                for ($i = 0, $count = count($tokens); $i < $count; $i ++)
                {
                        $token = &$tokens[$i];
                        switch (strtolower($token))
                        {
                                case 'eq':
                                        $token = '==';
                                        break;

                                case 'ne':
                                case 'neq':
                                        $token = '!=';
                                        break;

                                case 'lt':
                                        $token = '<';
                                        break;

                                case 'le':
                                case 'lte':
                                        $token = '<=';
                                        break;

                                case 'gt':
                                        $token = '>';
                                        break;

                                case 'ge':
                                case 'gte':
                                        $token = '>=';
                                        break;

                                case 'and':
                                        $token = '&&';
                                        break;

                                case 'or':
                                        $token = '||';
                                        break;

                                case 'not':
                                        $token = '!';
                                        break;

                                case 'mod':
                                        $token = '%';
                                        break;
                                // case '@':
                                // $token = '==';
                                // break;
                                // case 'first':
                                // $token = $this->get_val('smarty.foreach.first');
                                // break;
                                // case 'last':
                                // $token = $this->get_val('smarty.foreach.last');
                                // break;
                                default:
                                        if ($token[0] == '$')
                                        {
                                                $token = $this->get_val(substr($token, 1));
                                        }
                                        break;
                        }
                }

                if ($elseif)
                {
                        return '<?php elseif (' . implode(' ', $tokens) . '): ?>';
                }
                else
                {
                        return '<?php if (' . implode(' ', $tokens) . '): ?>';
                }
        }

        /**
         * 处理foreach标签
         * 
         * @author xiaotangren <unphp@qq.com>
         * @access public
         * @param string $tag_args        	
         * @return string
         */
        protected function _compile_foreach_start($tag_args)
        {
                //
                $from          = null;
                $key           = null;
                $item          = null;
                $_foreach_name = null;
                // {{foreach $log_list as $v}}
                // ...
                // {{/foreach}}

                if ('from' !== substr($tag_args, 0, 4))
                {
                        $pos           = strrpos($tag_args, ' as ');
                        $_foreach_name = substr(trim(substr($tag_args, 0, $pos)), 1);
                        $from          = $this->get_val($_foreach_name);
                        $item_key      = trim(substr($tag_args, $pos + 4));
                        if (($pos           = strrpos($item_key, '=>')) > 0)
                        {
                                $item = substr(trim(substr($item_key, 0, $pos)), 1);
                                $key  = substr(trim(substr($item_key, $pos + 2)), 1);
                        }
                        else
                        {
                                $item = substr(trim($item_key), 1);
                        }
                }
                // {{foreach from=$cate_list item=v key=k}}
                // ...
                // {{/foreach}}
                else
                {
                        $temp_array    = array();
                        $attrs         = $this->get_para($tag_args, 2, $temp_array);
                        $from          = $attrs['from'];
                        $item          = $attrs['item'];
                        $key           = empty($attrs['key']) ? null : $attrs['key'];
                        $_foreach_name = $temp_array['from'];
                }

                // 键值
                if (isset($this->_var[$item]) && !isset($this->_patchstack[$item]))
                {
                        $this->_patchstack[$item] = $item . '_' . str_replace(array(
                                    ' ',
                                    '.'
                                        ), '_', microtime());
                        $item                     = $this->_patchstack[$item];
                }
                else
                {
                        $this->_patchstack[$item] = $item;
                }
                //
                $item_part                          = $this->get_val($item);
                $this->_foreach_val_map[$item_part] = $_foreach_name;
                // 键名
                $key_part                           = '';
                if (!empty($key))
                {
                        if (isset($this->_var[$key]) && !isset($this->_patchstack[$key]))
                        {
                                $this->_patchstack[$key] = $key . '_' . str_replace(array(
                                            ' ',
                                            '.'
                                                ), '_', microtime());
                                $key                     = $this->_patchstack[$key];
                        }
                        else
                        {
                                $this->_patchstack[$key] = $key;
                        }
                        $key_part = $this->get_val($key) . ' => ';
                }

                if (!empty($attrs['name']))
                {
                        $name = $attrs['name'];
                }
                else
                {
                        $name = null;
                }

                $output = '<?php ';
                $output .= "\$_from = {$from}; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); };";
                if (!empty($name))
                {
                        $foreach_props = "\$this->_foreach['$name']";
                        $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
                        $output .= "if ({$foreach_props}['total'] > 0):\n";
                        $output .= "    foreach (\$_from AS $key_part$item_part):\n";
                        $output .= "        {$foreach_props}['iteration']++;\n";
                }
                else
                {
                        // $output .= "if (count(\$_from)):\n";
                        // $output .= " foreach (\$_from AS $key_part$item_part):\n";
                        $foreach_props = "\$this->_foreach['$_foreach_name']";
                        $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
                        $output .= "if ({$foreach_props}['total'] > 0):\n";
                        $output .= "    foreach (\$_from AS $key_part$item_part):\n";
                        $output .= "        {$foreach_props}['iteration']++;\n";
                }
                return $output . '?>';
        }

        /**
         * 处理smarty开头的预定义变量
         *
         * @access public
         * @param array $indexes        	
         *
         * @return string
         */
        protected function _compile_smarty_ref(&$indexes)
        {
                /* Extract the reference name. */
                $_ref = $indexes[0];

                switch ($_ref)
                {
                        case 'now':
                                $compiled_ref = 'time()';
                                break;

                        case 'foreach':
                                array_shift($indexes);
                                $_var      = $indexes[0];
                                $_propname = $indexes[1];
                                switch ($_propname)
                                {
                                        case 'index':
                                                array_shift($indexes);
                                                $compiled_ref = "(\$this->_foreach['$_var']['iteration'] - 1)";
                                                break;

                                        case 'first':
                                                array_shift($indexes);
                                                $compiled_ref = "(\$this->_foreach['$_var']['iteration'] <= 1)";
                                                break;

                                        case 'last':
                                                array_shift($indexes);
                                                $compiled_ref = "(\$this->_foreach['$_var']['iteration'] == \$this->_foreach['$_var']['total'])";
                                                break;

                                        case 'show':
                                                array_shift($indexes);
                                                $compiled_ref = "(\$this->_foreach['$_var']['total'] > 0)";
                                                break;

                                        default:
                                                $compiled_ref = "\$this->_foreach['$_var']";
                                                break;
                                }
                                break;

                        case 'get':
                                $compiled_ref = '$_GET';
                                break;

                        case 'post':
                                $compiled_ref = '$_POST';
                                break;

                        case 'cookies':
                                $compiled_ref = '$_COOKIE';
                                break;

                        case 'env':
                                $compiled_ref = '$_ENV';
                                break;

                        case 'server':
                                $compiled_ref = '$_SERVER';
                                break;

                        case 'request':
                                $compiled_ref = '$_REQUEST';
                                break;

                        case 'session':
                                $compiled_ref = '$_SESSION';
                                break;

                        default:
                                // $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
                                break;
                }
                array_shift($indexes);

                return $compiled_ref;
        }

        protected function smarty_insert_scripts($args)
        {
                static $scripts = array();

                $arr = explode(',', str_replace(' ', '', $args['files']));

                $str = '';
                foreach ($arr as $val)
                {
                        if (in_array($val, $scripts) == false)
                        {
                                $scripts[] = $val;
                                if ($val{0} == '.')
                                {
                                        $str .= '<script type="text/javascript" src="' . $val . '"></script>';
                                }
                                else
                                {
                                        $str .= '<script type="text/javascript" src="js/' . $val . '"></script>';
                                }
                        }
                }

                return $str;
        }

        protected function insert_mod($name) // 处理动态内容
        {
                list ($fun, $para) = explode('|', $name);
                $para = unserialize($para);
                if (stristr($fun, '::'))
                {
                        $exp        = explode('::', $fun);
                        $class_name = $exp[0];
                        $site_name  = ucfirst(strtolower(str_replace('_', '', $GLOBALS['_CFG']['site_code'])));
                        $dif_class  = $class_name . $site_name;
                        $dif_file   = Yaf_Registry::get("app")->library . $this->_ds . 'Diff' . $this->_ds . $dif_class . '.php';
                        if (file_exists($dif_file))
                        {
                                $class_name = 'Diff_' . $dif_class;
                        }
                        else
                        {
                                $class_name = 'Cls_' . $class_name;
                        }
                        return $class_name::$exp[1]($para);
                }
                else
                {
                        $fun = 'insert_' . $fun;
                        return $fun($para);
                }
        }

        protected function str_trim($str)
        {
                /* 处理'a=b c=d k = f '类字符串，返回数组 */
                while (strpos($str, '= ') != 0)
                {
                        $str = str_replace('= ', '=', $str);
                }
                while (strpos($str, ' =') != 0)
                {
                        $str = str_replace(' =', '=', $str);
                }

                return explode(' ', trim($str));
        }

        protected function html_options($arr)
        {
                $selected = $arr['selected'];

                if ($arr['options'])
                {
                        $options = (array) $arr['options'];
                }
                elseif ($arr['output'])
                {
                        if ($arr['values'])
                        {
                                foreach ($arr['output'] as $key => $val)
                                {
                                        $options["{$arr[values][$key]}"] = $val;
                                }
                        }
                        else
                        {
                                $options = array_values((array) $arr['output']);
                        }
                }
                if ($options)
                {
                        foreach ($options as $key => $val)
                        {
                                $out .= $key == $selected ? "<option value=\"$key\" selected>$val</option>" : "<option value=\"$key\">$val</option>";
                        }
                }

                return $out;
        }

        protected function html_select_date($arr)
        {
                $pre = $arr['prefix'];
                if (isset($arr['time']))
                {
                        if (intval($arr['time']) > 10000)
                        {
                                $arr['time'] = gmdate('Y-m-d', $arr['time'] + 8 * 3600);
                        }
                        $t     = explode('-', $arr['time']);
                        $year  = strval($t[0]);
                        $month = strval($t[1]);
                        $day   = strval($t[2]);
                }
                $now = gmdate('Y', $this->_nowtime);
                if (isset($arr['start_year']))
                {
                        if (abs($arr['start_year']) == $arr['start_year'])
                        {
                                $startyear = $arr['start_year'];
                        }
                        else
                        {
                                $startyear = $arr['start_year'] + $now;
                        }
                }
                else
                {
                        $startyear = $now - 3;
                }

                if (isset($arr['end_year']))
                {
                        if (strlen(abs($arr['end_year'])) == strlen($arr['end_year']))
                        {
                                $endyear = $arr['end_year'];
                        }
                        else
                        {
                                $endyear = $arr['end_year'] + $now;
                        }
                }
                else
                {
                        $endyear = $now + 3;
                }

                $out = "<select name=\"{$pre}Year\">";
                for ($i = $startyear; $i <= $endyear; $i ++)
                {
                        $out .= $i == $year ? "<option value=\"$i\" selected>$i</option>" : "<option value=\"$i\">$i</option>";
                }
                if ($arr['display_months'] != 'false')
                {
                        $out .= "</select>&nbsp;<select name=\"{$pre}Month\">";
                        for ($i = 1; $i <= 12; $i ++)
                        {
                                $out .= $i == $month ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
                        }
                }
                if ($arr['display_days'] != 'false')
                {
                        $out .= "</select>&nbsp;<select name=\"{$pre}Day\">";
                        for ($i = 1; $i <= 31; $i ++)
                        {
                                $out .= $i == $day ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
                        }
                }

                return $out . '</select>';
        }

        protected function html_radios($arr)
        {
                $name    = $arr['name'];
                $checked = $arr['checked'];
                $options = $arr['options'];

                $out = '';
                foreach ($options as $key => $val)
                {
                        $out .= $key == $checked ? "<input type=\"radio\" name=\"$name\" value=\"$key\" checked>&nbsp;{$val}&nbsp;" : "<input type=\"radio\" name=\"$name\" value=\"$key\">&nbsp;{$val}&nbsp;";
                }

                return $out;
        }

        protected function html_select_time($arr)
        {
                $pre = $arr['prefix'];
                if (isset($arr['time']))
                {
                        $arr['time'] = gmdate('H-i-s', $arr['time'] + 8 * 3600);
                        $t           = explode('-', $arr['time']);
                        $hour        = strval($t[0]);
                        $minute      = strval($t[1]);
                        $second      = strval($t[2]);
                }
                $out = '';
                if (!isset($arr['display_hours']))
                {
                        $out .= "<select name=\"{$pre}Hour\">";
                        for ($i = 0; $i <= 23; $i ++)
                        {
                                $out .= $i == $hour ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
                        }

                        $out .= "</select>&nbsp;";
                }
                if (!isset($arr['display_minutes']))
                {
                        $out .= "<select name=\"{$pre}Minute\">";
                        for ($i = 0; $i <= 59; $i ++)
                        {
                                $out .= $i == $minute ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>";
                        }

                        $out .= "</select>&nbsp;";
                }
                if (!isset($arr['display_seconds']))
                {
                        $out .= "<select name=\"{$pre}Second\">";
                        for ($i = 0; $i <= 59; $i ++)
                        {
                                $out .= $i == $second ? "<option value=\"$i\" selected>" . str_pad($i, 2, '0', STR_PAD_LEFT) . "</option>" : "<option value=\"$i\">$i</option>";
                        }

                        $out .= "</select>&nbsp;";
                }

                return $out;
        }

        protected function cycle($arr)
        {
                static $k, $old;

                $value = explode(',', $arr['values']);
                if ($old != $value)
                {
                        $old = $value;
                        $k   = 0;
                }
                else
                {
                        $k ++;
                        if (!isset($old[$k]))
                        {
                                $k = 0;
                        }
                }

                echo $old[$k];
        }

        protected function make_array($arr)
        {
                $out = '';
                foreach ($arr as $key => $val)
                {
                        if ($val{0} == '$')
                        {
                                $out .= $out ? ",'$key'=>$val" : "array('$key'=>$val";
                        }
                        else
                        {
                                $out .= $out ? ",'$key'=>'$val'" : "array('$key'=>'$val'";
                        }
                }

                return $out . ')';
        }

        protected function smarty_create_pages($params)
        {
                $str = '';
                extract($params);
                if (empty($page))
                {
                        $page = 1;
                }
                $page = intval($page);
                if (empty($count) || empty($size) || empty($pageUrl))
                {
                        return $str;
                }
                else
                {
                        if ($count <= $size)
                        {
                                return $str;
                        }
                        $total    = ceil($count / $size);
                        $pageSize = 6;
                        $start    = 0;
                        $end      = 0;
                        if ($total < $pageSize)
                        {
                                $start = 1;
                                $end   = $total;
                        }
                        else
                        {
                                $start = $page - 3;
                                $end   = $page + 3;
                                if ($page - 3 < 0)
                                {
                                        $start = 1;
                                        $end   = $pageSize + 1;
                                }
                                if ($page + 3 > $total)
                                {
                                        $start = $total - $pageSize;
                                        $end   = $total;
                                }
                        }
                        $firstStatus = $page === 1 ? 'disabled' : '';
                        $str .= '<ul class="pagination">';
                        $str .= '<li class="' . $firstStatus . '">';
                        $str .= '<a href="' . $pageUrl . '/page/1"><span>&laquo;</span></a>';
                        $str .= '</li>';
                        for ($i = $start; $i <= $end; $i ++)
                        {
                                $status = $page === $i ? 'disabled' : '';
                                $str .= '<li class="' . $status . '">';
                                $str .= '<a href="' . $pageUrl . '/page/' . $i . '">' . $i . '</a>';
                                $str .= '</li>';
                        }
                        $lastStatus = $page === $total ? 'disabled' : '';
                        $str .= '<li class="' . $lastStatus . '">';
                        $str .= '<a href="' . $pageUrl . '/page/' . $total . '"><span>&raquo;</span></a>';
                        $str .= '</li>';
                        $str .= '</ul>';
                }
                return $str;
        }

        protected function autoMkdir($file)
        {
                $pathinfo = pathinfo($file);
                if (!empty($pathinfo['dirname']))
                {
                        if (file_exists($pathinfo['dirname']) === false)
                        {
                                if (@mkdir($pathinfo['dirname'], 0777, true) === false)
                                {
                                        return false;
                                }
                                chmod($pathinfo['dirname'], 0777);
                        }
                }
        }

        protected function getCachefilepath($filename, $cache_id)
        {
                $filename  = str_replace($this->_ds, '_', $filename);
                $cachename = basename($filename, strrchr($filename, '.')) . '_' . $cache_id;
                $hash_dir  = $this->cache_dir . substr(md5($cachename), 0, 2) . '/' . substr(md5($cachename), 3, 2);
                $cachefile = $hash_dir . '/' . $cachename . '.php';
                return $cachefile;
        }

        protected function doCreateCache($filename, $cache_id, $out)
        {
                $cachefile = $this->getCachefilepath($filename, $cache_id);
                $data      = serialize(array(
                    'template' => $this->template,
                    'expires' => $this->_nowtime + $this->cache_lifetime,
                    'maketime' => $this->_nowtime
                ));
                while (strpos($out, "\n\n") !== false)
                {
                        $out = str_replace("\n\n", "\n", $out);
                }
                $this->autoMkdir($cachefile);
                if (file_put_contents($cachefile, '<?php exit;?>' . $data . $out, LOCK_EX) === false)
                {
                        trigger_error('can\'t write:' . $cachefile);
                }
                return $out;
        }

        protected function escapeTag($tag)
        {
                $escapeList = array('{', '}');
                foreach ($escapeList as $value)
                {
                        $tag = str_replace($value, '\\' . $value, $tag);
                }
                return $tag;
        }

}
